package net.pragyah.scalby.dseparation


import junit.framework.TestCase
import junit.framework.Assert._

import net.pragyah.scalgorithms.graphs._

class TestDSeparationAlgoGVP  extends TestCase{
  
  def testFind_reachable_nodes = {
    
    val algo = new DSeparationAlgoGVP()
    
    //from page 82 'Learning Bayesian Networks' by Richard E. Neapolitan
    val X = Vertex[char]('X');
    val Y = Vertex[char]('Y');
    val Z = Vertex[char]('Z');
    val W = Vertex[char]('W');
    val U = Vertex[char]('U');
    val V = Vertex[char]('V');
    val S = Vertex[char]('S');
    val T = Vertex[char]('T');
    val Q = Vertex[char]('Q');
    val M = Vertex[char]('M');
    val N = Vertex[char]('N');

    val graph:Graph[char] = Graph[char](Nil,true)
    val all = X::Y::Z::W::U::V::S::T::Q::M::N::Nil 
    graph ++  all//adding all the vertices
    //adding all the edges
      graph.addEdge(X,Y)
	graph.addEdge(X,Z)
	graph.addEdge(X,U)
	graph.addEdge(Y,V)
	graph.addEdge(W,S)
	graph.addEdge(W,X)
	graph.addEdge(U,T)
	graph.addEdge(V,Z)
	graph.addEdge(V,Q)
	graph.addEdge(S,W)
	graph.addEdge(S,V)
	graph.addEdge(T,M)
	graph.addEdge(M,S)
	graph.addEdge(M,N)

    
    def isLegal(G:Graph[char])(u:Vertex[char],v:Vertex[char],w:Vertex[char]):boolean =  (u,v,w) match{
      case (X,Y,V) => true
      case (Y,V,Q) => true
      case (X,W,S) => true
      case (X,U,T) => true
      case (U,T,M) => true
      case (T,M,S) => true
      case (M,S,V) => true
      case (S,V,Q) => true
      case _ => false
    }
    
    
    val R = algo.find_reachable_nodes(graph,Set(X),isLegal(graph)_)
    
    assert(R.contains(X))
    assert(R.contains(Y))
    assert(R.contains(U))
    assert(R.contains(V))
    assert(R.contains(S))
    assert(R.contains(T))
    assert(R.contains(Q))
    assert(R.contains(M))
    assert(R.contains(Z)) // this one is printed wrongly in the book ... z is reachable from X
    assert(!R.contains(W))
    assert(!R.contains(N))
    
    assert(graph.getEdge(X,Y).get.weight == 1)
    assert(graph.getEdge(X,U).get.weight == 1)
    assert(graph.getEdge(X,Z).get.weight == 1) // this one is printed wrongly in the book as not-reachable... .. it should be 1 ... 
    assert(graph.getEdge(Y,V).get.weight == 2)
    assert(graph.getEdge(U,T).get.weight == 2)
    assert(graph.getEdge(T,M).get.weight == 3)
    assert(graph.getEdge(V,Q).get.weight == 3) // this is printed wrongly in the book as 6 .. it should be 3
    assert(graph.getEdge(M,S).get.weight == 4)
    assert(graph.getEdge(S,V).get.weight == 5)
    
    assert(graph.getEdge(V,Z).get.weight == 0)
    assert(graph.getEdge(M,N).get.weight == 0)
    assert(graph.getEdge(W,X).get.weight == 0)
    assert(graph.getEdge(W,S).get.weight == 0)
    assert(graph.getEdge(S,W).get.weight == 0)
    
    
  }
  
  
  def testIsOrHasDescInA(){
    
    
    val algo = new DSeparationAlgoGVP()
    
    //from page 77 'Learning Bayesian Networks' by Richard E. Neapolitan
    val X = Vertex[char]('X');
    val Y = Vertex[char]('Y');
    val Z = Vertex[char]('Z');
    val W = Vertex[char]('W');
    val U = Vertex[char]('U');
    val V = Vertex[char]('V');
    val S = Vertex[char]('S');
    val T = Vertex[char]('T');
    val Q = Vertex[char]('Q');
    val M = Vertex[char]('M');
    val N = Vertex[char]('N');

    val graph:Graph[char] = Graph[char](Nil,true)
    val all = X::Y::Z::W::U::V::S::T::Q::M::N::Nil 
    graph ++  all//adding all the vertices

	graph.addEdge(X,Y)
	graph.addEdge(X,Z)
	graph.addEdge(X,U)
	graph.addEdge(Y,V)
	graph.addEdge(W,S)
	graph.addEdge(W,X)
	graph.addEdge(U,T)
	graph.addEdge(V,Z)
	graph.addEdge(V,Q)
	graph.addEdge(S,W)
	graph.addEdge(S,V)
	graph.addEdge(T,M)
	graph.addEdge(M,S)
	graph.addEdge(M,N)
    
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    
    // X
    var A = Set(X)
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assert(algo.isOrHasDescInA(X,A))

    A = Set(Y)
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assert(algo.isOrHasDescInA(X,A))
    
    A = Set(V)
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assert(algo.isOrHasDescInA(X,A))

    A = Set(Q)
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assert(algo.isOrHasDescInA(X,A))

    A = Set(Z)
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assert(algo.isOrHasDescInA(X,A))

    A = Set(W)
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assert(algo.isOrHasDescInA(X,A))

    A = Set(S)
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assert(algo.isOrHasDescInA(X,A))

    // W
    A = Set(Z)
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assert(algo.isOrHasDescInA(W,A))
 
    
    // Z
    A = Set(X)
    A += Y
    A += V
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assertFalse(algo.isOrHasDescInA(Z,A))
    
    A = Set(N)
    A += S
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assertFalse(algo.isOrHasDescInA(V,A))
    
    A = Set(N)
    graph.vertices.foreach(_.tag = (false,false,false)) //required ... 
    assert(algo.isOrHasDescInA(S,A))
    
  }
  
  
  def testIsLegal(){

    val algo = new DSeparationAlgoGVP()

    val U = Vertex[char]('U');
    val V = Vertex[char]('V');
    val W = Vertex[char]('W');
    val all = U::V::W::Nil 
    
    U.tag = (false,false,true)
    V.tag = (false,false,true) // in[V] is false
    V.tag = (false,false,true)
    
    var graph:Graph[char] = Graph[char](Nil,true)
    graph ++  all//adding all the vertices
    //head to tail
    graph.addEdge(U,V)
    graph.addEdge(V,W)

    
    assert(!algo.isLegal(graph,Set())(U,V,U)) //U == W
    assert(algo.isLegal(graph,Set())(U,V,W)) // U != W and head to tail

    graph = Graph[char](Nil,true)
    graph ++  all//adding all the vertices
	//taiil to head
    graph.addEdge(V,U)
    graph.addEdge(W,V)
    assert(algo.isLegal(graph,Set())(U,V,W)) // U != W and tail to head
    
    graph = Graph[char](Nil,true)
    graph ++  all//adding all the vertices
    //taiil to tail
    graph.addEdge(V,U)
    graph.addEdge(V,W)
    assert(algo.isLegal(graph,Set())(U,V,W)) // U != W and tail to tail
    

    graph = Graph[char](Nil,true)
    graph ++  all//adding all the vertices
    //head to head ... with inOrDesc[V] false ... which makes them independent 
    graph.addEdge(U,V)
    graph.addEdge(W,V)
    assert(!algo.isLegal(graph,Set())(U,V,W)) // U != W and head to head
    
    
    V.tag = (false,true,true) // in[V] is true
    assert(algo.isLegal(graph,Set())(U,V,W)) // U != W and head to head
    
    
    V.tag = (true,true,true) // in[V] is true
    assert(algo.isLegal(graph,Set())(U,V,W)) // U != W and head to head
    
    V.tag = (true,true,true) // in[V] is true
    assert(!algo.isLegal(graph,Set()+graph.getEdge(U,V).get)(U,V,W)) // U != W and head to head
    
  }
  
  
  
  
 def testFindDSeparations = {
    
   
   /*			  X
    * 			/	\
    * 		 |/		 \|
    * W	---> Y		 Z 
    * 		  \     /  \
    * 			\||/     \|
    * 			  R ----> S
    * 			  |
    *  			  \/
    * 			  T
    */
   
   
   
    val algo = new DSeparationAlgoGVP()
    
   //from page 76 'Learning Bayesian Networks' by Richard E. Neapolitan
    //EXAMPLE 2.2
    val X = Vertex[char]('X');
    val Y = Vertex[char]('Y');
    val Z = Vertex[char]('Z');
    val R = Vertex[char]('R');
    val S = Vertex[char]('S');
    val T = Vertex[char]('T');
    val W = Vertex[char]('W');
    
    
    val graph:Graph[char] = Graph[char](Nil,true)
    val all = X::Y::Z::R::S::T::W::Nil 
    graph ++  all//adding all the vertices
	graph.addEdge(W,Y)
	graph.addEdge(Y,R)
	graph.addEdge(R,T)
	graph.addEdge(R,S)
	graph.addEdge(X,Y)
	graph.addEdge(X,Z)
	graph.addEdge(Z,R)
	graph.addEdge(Z,S)

    var A = Set[Vertex[char]]() + Y + Z
    var B = Set[Vertex[char]]() + X
    var D = algo.findDSeparations(graph,A,B)
    // X and R are d-separated by {Y , Z}
    assert(D.contains(R)) //Example 2.2 - 1  
    //X and T are d-separated by {Y , Z}
    assert(D.contains(T)) //Example 2.2 - 2
    assert(D.contains(S)) 
    

    A = Set[Vertex[char]]() + R
    B = Set[Vertex[char]]() + W
    D = algo.findDSeparations(graph,A,B)
    //W and T are d-separated by {R}
    assert(D.contains(T)) //Example 2.2 - 3
    assert(!D.contains(S))

    A = Set[Vertex[char]]() + X
    B = Set[Vertex[char]]() + Y
    D = algo.findDSeparations(graph,A,B)
    //Y and Z are deseparated by {X}
    assert(D.contains(Z)) //Example 2.2 - 4
    
    
    A = Set[Vertex[char]]() + R + Z
    B = Set[Vertex[char]]() + W
    D = algo.findDSeparations(graph,A,B)
    //W and S are d-separated by {R,Z}
    assert(D.contains(S)) //Example 2.2 - 5
    assert(D.contains(T)) 
    
    A = Set[Vertex[char]]() + Y + Z
    B = Set[Vertex[char]]() + W
    D = algo.findDSeparations(graph,A,B)
    //W and S are also d-separated by { Y , Z}
    assert(D.contains(S)) //Example 2.2 - 6
    assert(D.contains(R)) //Example 2.2 - 6
    
    
    A = Set[Vertex[char]]() + Y + X
    B = Set[Vertex[char]]() + W
    D = algo.findDSeparations(graph,A,B)
    //W and S are also d-separated by{Y , X} 
    assert(D.contains(S)) //Example 2.2 - 7
    assert(D.contains(R))
    assert(D.contains(Z))
    assert(D.contains(T))
    
    
    A = Set[Vertex[char]]()
    B = Set[Vertex[char]]() + W
    D = algo.findDSeparations(graph,A,B)
    //W and X are d-separated by {}
    assert(D.contains(X)) //Example 2.2 - 8
    assert(D.contains(Z)) 
    
    A = Set[Vertex[char]]() + Y
    B = Set[Vertex[char]]() + W
    D = algo.findDSeparations(graph,A,B)
    //W and X are not d-separated by {Y}
    assert(!D.contains(X)) //Example 2.2 - 9
    assert(!D.contains(Z)) 
    assert(!D.contains(R)) 
    assert(!D.contains(S)) 
    assert(!D.contains(T)) //this is the next one anyways    
    
    A = Set[Vertex[char]]() + Y
    B = Set[Vertex[char]]() + W
    D = algo.findDSeparations(graph,A,B)
    //W and T are not d-separated by {Y }
    assert(!D.contains(T)) //Example 2.2 - 10
    
    
    
    
    
    
    
    
    
    
    
 }
}
